import random
from string import ascii_lowercase, ascii_uppercase, digits, punctuation

from django.contrib.auth.password_validation import validate_password
from django.contrib.auth.models import (
    BaseUserManager,
    AbstractUser
)
from django.db import models, connection
import uuid
from pgcrypto import fields
from localflavor.us.models import USZipCodeField, USStateField
from django_tenants.models import TenantMixin, DomainMixin

from apps.billing.types import INVOICE_STATUS_CHOICES, InvoiceStatus
from apps.users.types import PROVIDER_STATUS_TYPE_CHOICES, ProviderStatusTypeChoices, PromoCreditStatusType, \
    PROMO_CREDIT_STATUS_TYPE, ETHERA_CREDIT_STATUS_TYPE, EtheraCreditStatusType, PROVIDER_LICENSE_CHOICES, \
    ProviderLicenseType
from django.conf import settings

DEFAULT_PROMO = 50.00
DEFAULT_CANCELLATIONS = 10


def user_image_path(instance, filename):
    if instance:
        user_id = instance.id
        return f"{settings.BUCKET_BLOB_PATH}/ProfilePictures/user_{user_id}/{filename}"


class UserManager(BaseUserManager):

    def _create_user(self, email, password, **extra_fields):
        if 'groups' in extra_fields:
            extra_fields.pop('groups')

        if 'user_permissions' in extra_fields:
            extra_fields.pop('user_permissions')

        if not email:
            raise ValueError('The given email must be set.')

        email = self.normalize_email(email)
        if extra_fields['is_superuser']:
            user = self.model(email=email, **extra_fields)
            s_user = SystemAccounts.objects.create(email=email, user_ptr=user, is_staffs=True)
            validate_password(password)
            s_user.set_password(password)
            s_user.username = email
            s_user.is_active = True
            s_user.is_super_user=True
            s_user.is_staff=True
            s_user.save()
            return user
        else:
            user = self.model(email=email, **extra_fields)
            validate_password(password)
            user.set_password(password)
            user.username = email
            user.is_active = True
            user.save(using=self._db)
            return user

    def create_user(self, email=None, password=None, **extra_fields):
        extra_fields.setdefault('is_staff', False)
        extra_fields.setdefault('is_superuser', False)
        return self._create_user(email, password, **extra_fields)

    def create_superuser(self, email, password, **extra_fields):
        extra_fields.setdefault('is_staff', True)
        extra_fields.setdefault('is_superuser', True)

        if extra_fields.get('is_staff') is not True:
            raise ValueError('Superuser must have is_staff=True.')
        if extra_fields.get('is_superuser') is not True:
            raise ValueError('Superuser must have is_superuser=True')

        return self._create_user(email, password, **extra_fields)

    def make_random_password(self, length=10):
        rand_pass = []
        for i in range(0, 2):
            rand_pass.append(random.choice(digits))
            rand_pass.append(random.choice(ascii_lowercase))
            rand_pass.append(random.choice(punctuation))
            rand_pass.append(random.choice(ascii_uppercase))

        random.shuffle(rand_pass)
        password = ''.join(rand_pass)
        return password


class User(AbstractUser):
    id = models.UUIDField(primary_key=True,
                          default=uuid.uuid4,
                          editable=False)
    username = fields.CharPGPSymmetricKeyField(max_length=150, default="")
    email = fields.EmailPGPSymmetricKeyField(unique=True)
    avatar = models.ImageField(upload_to=user_image_path, null=True, blank=True)
    first_name = fields.CharPGPSymmetricKeyField(
        null=True, blank=True, max_length=30)
    middle_name = fields.CharPGPSymmetricKeyField(
        null=True, blank=True, max_length=30)
    last_name = fields.CharPGPSymmetricKeyField(
        null=True, blank=True, max_length=30)
    is_provider = models.BooleanField(default=False)
    is_client = models.BooleanField(default=False)
    is_first_login = models.BooleanField(default=True)
    phone_number = fields.CharPGPSymmetricKeyField(
        null=True, blank=True, max_length=15)
    date_of_birth = fields.DateTimePGPSymmetricKeyField(null=True, blank=True)
    address = fields.CharPGPSymmetricKeyField(
        null=True, blank=True, max_length=255)
    state = USStateField(null=True, blank=True)
    city = models.CharField(max_length=50, null=True, blank=True)
    zipcode = USZipCodeField(null=True, blank=True)
    is_active = models.BooleanField(default=True)

    # The `USERNAME_FIELD` property tells us which field we will use to log in.
    # In this case we want it to be the email field.
    USERNAME_FIELD = 'email'
    REQUIRED_FIELDS = []

    objects = UserManager()

    def __def__(self):
        """
        Returns a string representation of this `User`.
        This string is used when a `User` is printed in the console.
        """
        return self.email


class SystemAccounts(User):
    is_staffs = models.BooleanField(default=False)
    is_welcome_board = models.BooleanField(default=False)
    is_kiosk = models.BooleanField(default=False)

    class Meta:
        db_table='system_accounts'


# from django.db.models.signals import post_save
# from django.dispatch import receiver
# from django.contrib.auth import get_user_model
#
# @receiver(post_save, sender=get_user_model())
# def create_system_account(sender, instance, created, **kwargs):
#     if created and instance.is_superuser:
#         # Create SystemAccounts instance with is_staffs set to True
#         SystemAccounts.objects.create(user_ptr=instance, is_staffs=True)

class Provider(TenantMixin, User):
    status = fields.CharPGPSymmetricKeyField(
        choices=PROVIDER_STATUS_TYPE_CHOICES,
        default=ProviderStatusTypeChoices.PENDING.value,
        max_length=3)
    credit_card = fields.CharPGPSymmetricKeyField(null=True, max_length=255)
    stripe_account_id = models.CharField(max_length=255, null=True, blank=True)
    stripe_id = models.CharField(max_length=255, null=True)
    suffix = fields.CharPGPSymmetricKeyField(
        max_length=10, null=True, blank=True)
    practice = fields.TextPGPSymmetricKeyField(null=True)
    cancelation_balance = models.IntegerField(default=DEFAULT_CANCELLATIONS)
    ethera_credit = models.DecimalField(
        max_digits=10,
        decimal_places=2,
        default=0)
    promo_credit = models.DecimalField(
        max_digits=10,
        decimal_places=2,
        default=DEFAULT_PROMO)
    npi = fields.CharPGPSymmetricKeyField(
        null=True, blank=True, max_length=50)
    npi_two = fields.CharPGPSymmetricKeyField(null=True, blank=True, max_length=50)
    tax = fields.CharPGPSymmetricKeyField(null=True, blank=True, max_length=255)
    tax_two = fields.CharPGPSymmetricKeyField(null=True, blank=True, max_length=255)
    caqh = fields.CharPGPSymmetricKeyField(null=True, blank=True, max_length=50)
    website_link = models.CharField(max_length=255, null=True, blank=True)
    taxonomy = fields.CharPGPSymmetricKeyField(
        null=True, blank=True, max_length=50)
    created_at = models.DateTimeField(
        auto_now_add=True,
        editable=False,
        null=True,
        verbose_name=u'Created at')

    def delete(self, *args, **kwargs):
        # Delete associated tenant schema
        with connection.cursor() as cursor:
            cursor.execute(f'DROP SCHEMA IF EXISTS {self.schema_name} CASCADE;')

        super().delete(*args, **kwargs)

    class Meta:
        db_table = 'providers'


class ProviderLicense(models.Model):
    provider = models.ForeignKey(Provider, on_delete=models.CASCADE,
                                 related_name='provider_license')
    license_type = fields.CharPGPSymmetricKeyField(
        choices=PROVIDER_LICENSE_CHOICES,
        default=ProviderLicenseType.NONE.value,
        max_length=3)
    license_number = fields.CharPGPSymmetricKeyField(
        null=True, blank=True, max_length=50)
    expire_date = fields.DateTimePGPSymmetricKeyField(null=True, blank=True)
    other_license = models.CharField(null=True, blank=True, max_length=100)
    state = USStateField(null=True, blank=True)

    class Meta:
        db_table = 'providers_license'


class EtheraCredit(models.Model):
    provider = models.ForeignKey(Provider,
                                 on_delete=models.CASCADE,
                                 related_name='etheracredit')
    date = models.DateTimeField(auto_now_add=True)
    amount = models.DecimalField(max_digits=10,
                                 decimal_places=2, default=0.00)
    status = fields.CharPGPSymmetricKeyField(
        choices=ETHERA_CREDIT_STATUS_TYPE,
        default=EtheraCreditStatusType.ADD.value,
        max_length=3)

    notes = fields.CharPGPSymmetricKeyField(null=True,
                                            blank=True,
                                            max_length=255)
    created_at = models.DateTimeField(
        auto_now_add=True,
        editable=False,
        null=True,
        verbose_name=u'Created at')

    def save(self, *args, **kwargs):
        if self.__class__.objects.filter(provider=self.provider).count() > 10:
            self.__class__.objects.filter(
                pk__in=self.__class__.objects.filter(provider=self.provider).values_list('pk')[9:]).delete()
        return super(EtheraCredit, self).save(*args, **kwargs)

    class Meta:
        db_table = 'ethera_credit'


class PromoCredit(models.Model):
    provider = models.ForeignKey(Provider,
                                 on_delete=models.CASCADE,
                                 related_name='promocredit')
    date = models.DateTimeField(auto_now_add=True, null=True, blank=True)
    amount = models.DecimalField(max_digits=10,
                                 decimal_places=2, default=0.00)
    status = fields.CharPGPSymmetricKeyField(
        choices=PROMO_CREDIT_STATUS_TYPE,
        default=PromoCreditStatusType.ADD.value,
        max_length=3)

    created_at = models.DateTimeField(
        auto_now_add=True,
        editable=False,
        null=True,
        verbose_name=u'Created at')
    notes = fields.CharPGPSymmetricKeyField(null=True, blank=True, max_length=255)

    def save(self, *args, **kwargs):
        if self.__class__.objects.filter(provider=self.provider).count() > 10:
            self.__class__.objects.filter(
                pk__in=self.__class__.objects.filter(provider=self.provider).values_list('pk')[9:]).delete()
        return super(PromoCredit, self).save(*args, **kwargs)

    class Meta:
        db_table = 'promo_credit'


class ClientSchemaHandle(models.Model):
    provider = models.ForeignKey(Provider, on_delete=models.CASCADE)
    schema_name = models.CharField()
    client_id = models.UUIDField()

    class Meta:
        db_table = 'client_schema_handle'



class Domain(DomainMixin):
    pass
